/*******************************************
 * The SNIFFER GUI Operations Library
 * (SOURCE)
 * -----------------------------------
 *
 * C. Nunez 	(cnunez@stevens.edu)
 * W. Best		(wbest@stevens.edu)
 * K. Fuhrman 	(ktfuhrman@gmail.com)
 * M. DiAmore 	(mdiamore@gmail.com)
 *
 * Date created: 	13 May, 2010
 * Last edited:		13 May, 2010
 *
 * Summary
 * -------
 * This library provides all GUI functionality
 * for SNIFFER.
 *
 *******************************************/


#include "sniffer_gtk_gui.h"
#pragma warning(disable:4996)			//Disable strcpy safety warning, since strcpy_s is not std and is unportable.
using namespace std;

//============================================
//	GTK+ libraries.
//============================================
#include "gui_globals.h"

//------------------------------------
//	Prototypes
//------------------------------------

void in_startSniffing(const char *device);		//Internal packet capture init method


//------------------------------------
//Private globals
//------------------------------------
vector< vector<string> >	packetList;				//List of all packets received.
int scrollUntouched;
int scrollCounter;
int period = 50; //This is the number of milisecs in between ticks of the timer
int timeCount = 0;
int PACKETID = -1;
char packetData[10000];
int deviceChosen = 0;
char* deviceName;
bool promiscuous = FALSE;
//For debugging
int ID;
//======================
//	Definitions.
//======================

int _sniffer_gui_main(int argc, char *argv[])
{
	gui_set_running();					//Flag the GUI on.

	ID = 0;
	scrollUntouched = 1;
	scrollCounter = 0;

	gtk_init (&argc, &argv);

	//Initialize everything else (windows, buttons, containers, etc.)
	//---------------------------------------------------------------
	initializeEverything();	

	//Go!
	//---
	
	//Sets up the timer to update the list every so many miliseconds
	//If we do this fast enough, it will look good	
/*
	g_timeout_add(period, (GSourceFunc) time_handler, (gpointer) window);
	time_handler(window);	*/
	gtk_widget_show_all(startSession);
	gtk_main();

	return 0;
}

void enterThreads() {
	#ifdef WIN32
		gdk_threads_enter();
	#endif
}

void exitThreads() {
	#ifdef WIN32
		gdk_threads_leave();
	#endif
}

//Stop sniffing
void stopSniffing() {
	const char *fcn = "stopSniffing";
	if (gui_stop_capture() == 0)
		dcout(fcn,"Capture has stopped.");
	else
		dcout(fcn,"ERROR: Could not stop packet capture! ruh-roh");
}

//Quit the program
void quitProgram() {
	stopSniffing();
    gtk_main_quit();
	exit(0);
}

void startSniffing()				
{
	GThread		*captureThread;
	GError		*captureThreadError;

	//Create the thread
	//-----------------
	if ( (captureThread = g_thread_create((GThreadFunc)in_startSniffing, (void*)deviceName, FALSE, &captureThreadError)) == NULL)
	{
		dcout("startSniffing","Could not create capture thread!");
		return;
	}
}
//Start sniffing
void in_startSniffing(const char *device) {
	const char *fcn = "startSniffing";
	if (device != NULL)
	{
//Winpcap uses the description field, so its device ID needs to be retrieved.
//libpcap doesn't, so GUI can start capture just from pcap_if_t::name.
//---------------------------------------------------------------------------
#if defined(WIN32) || defined(_WIN32)
		if (gui_start_capture(get_device_from_name(device)) == 0)
#else
		if (gui_start_capture(device) == 0)
#endif
			dcout(fcn,"Capture has started.");
		else
		{
			dcout(fcn,"Error in starting capture!");
			quitProgram();
		}
	}
}

//Display help screen
void getHelp() {
	//Go to help screen
	gtk_widget_show(helpWindow);
}

void closeHelp() {
	gtk_widget_hide(helpWindow);
}

//Display about screen
void findOutAbout() {
	//Got to about screen
	gtk_widget_show(aboutWindow);
	if (gtk_dialog_run(GTK_DIALOG(aboutWindow)) == GTK_RESPONSE_CANCEL) {
		closeAbout();
      	}
}

void closeAbout() {
	gtk_widget_hide(aboutWindow);
}	

void togglePromiscuity() {
	stopSniffing();
	exitThreads();				//Restrict.
	if(promiscuous)
		setPromOff();
	else
		setPromOn();

	exitThreads();				//Release.
	startSniffing();
}

//UPDATE (cnunez):	Replaced C-style manipulations with string-based ones, as they are not thread-safe.
//TODO:				Make method to determine promiscuous mode, as this seems really hackish.
//-----------------------------------------------------------------------------------------------------
void setPromOn() {
	//Tell Sniffer to be promiscuous
	exitThreads();				//Restrict.
	string title((string)gtk_window_get_title(GTK_WINDOW(window)));					//Get the window title.
	size_t promItr = title.find(" (Promiscuous)");									//See if window is actually promiscuous settings window.
	if(promItr==string::npos) {														//If it's not there, include it.
		title+=" (Promiscuous)";
		gtk_window_set_title(GTK_WINDOW(window), title.c_str());					//Reset the window title.
	}
	gui_set_promisc();			//Set it if not running.
	gtk_widget_hide(promWindow);
	exitThreads();				//Release.
	promiscuous = TRUE;
}

void setPromOff() {
	//Tell sniffer to not be promiscuous
	exitThreads();
	string title((string)gtk_window_get_title(GTK_WINDOW(window)));					//Get the window title.
	size_t promItr = title.find(" (Promiscuous)");									//See if window is promiscuous window
	if(promItr!=string::npos) {
		title = title.substr(0, (int)promItr);										//Remove promiscuous from title.
		gtk_window_set_title(GTK_WINDOW(window), title.c_str());  
	}
	gui_set_promisc();		//Set promiscuous mode if running.
	gtk_widget_hide(promWindow);
	exitThreads();
	promiscuous = TRUE;
}

void startNewSession() {
	//exitThreads();			//Restrict.
	gtk_widget_hide(startSession);
	gtk_widget_show(deviceSelecter);
	//exitThreads();				//Release.
}

//Takes the button that called it, uses that to figure out when it was called
//If "resumePrevious" called it, that means it was called at the beginning, otherwise, during "normal" run
void resumePreviousSession(GtkWidget* sender) {
	char* sessionName;
	//gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(openSession), default_folder_for_saving);
	if (gtk_dialog_run (GTK_DIALOG (openSession)) == GTK_RESPONSE_ACCEPT) {
		sessionName = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(openSession));
		//Tell db what session to open
		gtk_widget_hide(openSession);
		gtk_widget_show(deviceSelecter);
	}
	else {
		gtk_widget_hide(openSession);
		if(sender==resumePrevious)
			gtk_widget_show(startSession);
	}
}

void saveSession() {
	enterThreads();
	char* sessionName;
	gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(nameNewSession), "Untitled_database.sql");     
     	if (gtk_dialog_run(GTK_DIALOG(nameNewSession)) == GTK_RESPONSE_ACCEPT) {
		sessionName = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(nameNewSession));
		
		if (save_db(sessionName, packetList) != 1)			//Save the session to DB.
		{
			cout << "** ERROR **: Save failed!" << endl;
		}

		gtk_widget_hide(nameNewSession);
		gtk_widget_show(deviceSelecter);
	}
	else {
		gtk_widget_hide(nameNewSession);
		gtk_widget_show(startSession);
	}
	exitThreads();
}

void fillDeviceList() {
	vector<string> devices;
	devices = select_device_gui(); //Get from Carlos' sniffer_core
	unsigned int numDevices = (int)devices.size();
	unsigned int i = 0;
	for(i=0; i<numDevices; i++) {
		gtk_list_store_append(deviceList, &deviceListItr);
		char *dev = (char *)devices[i].c_str();
		gtk_list_store_set(deviceList, &deviceListItr, 0, dev, -1);	
	}	
}

//Select sniffer source while SNIFFER is running.
void selectSource() {
	stopSniffing();				//Stop.
	//exitThreads();		//Enter critical section for GUI.
	gtk_window_set_title(GTK_WINDOW(window), "Sniffer has no device");
	//Doing this also clears the packet list and text buffer
	//Make a new tree store named paxList
	paxList = gtk_list_store_new(4, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);	
	//Tie the tree store and view together
	gtk_tree_view_set_model(GTK_TREE_VIEW(paxListView), GTK_TREE_MODEL(paxList));
	paxDatText = gtk_text_buffer_new(NULL);
	ID = 0;
	deviceChosen = 0;
	gtk_widget_show_all(deviceSelecter);
	//exitThreads();		//Release.
}

void deviceSelected() {	
	GtkTreeIter itr;
	GtkTreeModel *list;
	if (gtk_tree_selection_get_selected(listDevice, &list, &itr)) {
		gtk_tree_model_get(list, &itr, 0, &deviceName,  -1);
		deviceChosen = 1;
		gtk_widget_hide(deviceSelecter);
		char title[75];
		strcpy(title, "Sniffer listening to device: ");
		strcat(title, deviceName);
		printf("%s\n",title);
		//exitThreads();			//Restrict.
		gtk_window_set_title(GTK_WINDOW(window), title); 
		//exitThreads();			//Release.
		gtk_widget_show_all(window);	//Show.
		startSniffing();
	}
	else {
		deviceChosen = 0;
		printf("No device selected.\n");
	}
		
}

//Test buffer func
//----------------
void addToPacketList()
{
	const char *fcn	= "addToPacketList";
	gtk_list_store_prepend(paxList, &paxListItr);
	
	//Get information.
	//----------------
	string id	=	sniffer_pkt::Instance()->get_pkt_id();
	string src	=	sniffer_pkt::Instance()->get_src_ip();
	string dst	=	sniffer_pkt::Instance()->get_dst_ip();
	string info =	sniffer_pkt::Instance()->get_g_info();
	if (info == "")
		info = sniffer_pkt::Instance()->get_pkt_type();

	dcout(fcn,"Got packet: " + id + ", " + src + ", " + dst + ", " + info);
	//Store in list.
	//--------------
	gtk_list_store_set(paxList, &paxListItr, 0, atoi(id.c_str()), 1, (gchararray)src.c_str(), 2, (gchararray)dst.c_str(), 3, (gchararray)info.c_str(), -1);

	//now get the packet instance and push it to the packet list vector.
	//------------------------------------------------------------------
	packetList.push_back(sniffer_pkt::Instance()->get_sniffer_pkt());

	//Clear all
	//---------
	
}

void viewPacketData(int id) {
	//Get from packetList
	//-------------------
	string packetData;
	for (unsigned int i = 0; i < packetList.size(); i++)
	{
		unsigned int last	=	packetList[i].size()-1;
		if (atoi(packetList[i][0].c_str()) == id)
			packetData = packetList[i][last];
	}

	if (packetData.size() != 0)
		gtk_text_buffer_set_text(paxDatText, packetData.c_str(), packetData.length());
	else
	{
		packetData = "No data available for this packet.";		//Did this because gtk_text_buffer isn't dynamic.
		gtk_text_buffer_set_text(paxDatText, packetData.c_str(), packetData.length());
	}
	gtk_text_buffer_set_text(paxDatText, packetData.c_str(), packetData.length());
	gtk_text_view_set_buffer(GTK_TEXT_VIEW(paxDat), paxDatText);
}

//This is called when the user selects an item in the paxList
//It calls queryDatabase with the id of the selected packet
void packetSelected() {
	GtkTreeIter itr;
	GtkTreeModel *list;
	int paxID;
	if (gtk_tree_selection_get_selected(listPacket, &list, &itr)) {
		gtk_tree_model_get(list, &itr, 0, &paxID,  -1);
		viewPacketData(paxID);
	}
}

//Save the DB.
//------------
